﻿using System.Text;
using System.Xml;

namespace System.NETool;

public interface IConfigFile
{
    /// <summary>
    /// 获取配置文件名
    /// </summary>
    string FileName { get; }
}

/// <summary>
/// 定义一个接口，用于加载和保存XML配置
/// </summary>
public interface IXmlConfig : IConfigFile
{
    /// <summary>
    /// 从XML文件加载配置
    /// </summary>
    public void LoadFromXml();

    /// <summary>
    /// 保存配置到XML文件
    /// </summary>
    public void SaveToXml();
}

/// <summary>
/// 定义一个异步接口，用于异步加载和保存XML配置
/// </summary>
public interface IXmlConfigAsync : IConfigFile
{
    /// <summary>
    /// 异步从XML文件加载配置
    /// </summary>
    public Task LoadFromXmlAsync();

    /// <summary>
    /// 异步保存配置到XML文件
    /// </summary>
    public Task SaveToXmlAsync();
}

/// <summary>
/// 定义一个XmlConfig类，用于处理XML配置文件
/// </summary>
public class XmlConfig
{
    public string FileName { get; }

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="fileName">文件名</param>
    public XmlConfig(string fileName)
    {
        FileName = Dir.VerifyFileName(fileName);
    }

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="config"></param>
    public XmlConfig(IConfigFile config) : this(config.FileName)
    {
    }

    private XmlDocument _doc; // XML文档对象
    private XmlElement _root; // XML根元素

    /// <summary>
    /// 创建一个新的XML文档，并设置根元素
    /// </summary>
    /// <param name="rootName">根元素名称，默认为"Root"</param>
    public void New(string rootName = "Root")
    {
        _doc = new();
        XmlDeclaration dec = _doc.CreateXmlDeclaration("1.0", "utf-8", null);
        _doc.AppendChild(dec);
        _root = _doc.CreateElement(rootName);
        _doc.AppendChild(_root);
    }

    /// <summary>
    /// 加载XML文件
    /// </summary>
    public void Load()
    {
        if (!File.Exists(FileName))
        {
            throw new FileNotFoundException("The file does not exist.", FileName);
        }

        string text = File.ReadAllText(FileName);
        _doc = new();
        _doc.LoadXml(text);
        _root = _doc.DocumentElement;
    }

    /// <summary>
    /// 异步加载XML文件
    /// </summary>
    public async Task LoadAsync()
    {
        if (!File.Exists(FileName))
        {
            throw new FileNotFoundException("The file does not exist.", FileName);
        }

        string text = await File.ReadAllTextAsync(FileName);
        _doc = new();
        _doc.LoadXml(text);
        _root = _doc.DocumentElement;
    }

    /// <summary>
    /// 保存XML文件
    /// </summary>
    /// <returns>保存成功返回true，否则返回false</returns>
    public bool Save()
    {
        try
        {
            File.WriteAllText(FileName, _doc.XmlString(), Encoding.UTF8);
            return true;
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            return false;
        }
    }

    /// <summary>
    /// 异步保存XML文件
    /// </summary>
    /// <returns>保存成功返回true，否则返回false</returns>
    public async Task<bool> SaveAsync()
    {
        try
        {
            await File.WriteAllTextAsync(FileName, _doc.XmlString(), Encoding.UTF8);
            return true;
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            return false;
        }
    }

    /// <summary>
    /// 获取指定子节点的文本内容
    /// </summary>
    /// <param name="child">子节点名称</param>
    /// <returns>子节点的文本内容</returns>
    public string Get(string child)
    {
        if (_root == null)
        {
            throw new NullReferenceException("The root element is null. Please load the XML file first.");
        }

        return _root.Get(child);
    }

    /// <summary>
    /// 获取指定子节点的所有子节点列表
    /// </summary>
    /// <param name="child">子节点名称</param>
    /// <returns>子节点列表</returns>
    public XmlNodeList GetNodes(string child)
    {
        if (_root == null)
        {
            throw new NullReferenceException("The root element is null. Please load the XML file first.");
        }

        return _root.GetNodes(child);
    }

    /// <summary>
    /// 设置指定子节点的文本内容
    /// </summary>
    /// <param name="child">子节点名称</param>
    /// <param name="value">子节点的文本内容</param>
    /// <returns>设置后的子节点</returns>
    public XmlElement Set<T>(string child, T value) where T : notnull
    {
        if (_root == null)
        {
            throw new NullReferenceException("The root element is empty, please create a new XML file first.");
        }

        return _root.Set(child, value);
    }

    /// <summary>
    /// 添加一个新的子节点
    /// </summary>
    /// <param name="child">子节点名称</param>
    /// <returns>添加后的子节点</returns>
    public XmlElement NewNode(string child)
    {
        if (_root == null)
        {
            throw new NullReferenceException("The root element is empty, please create a new XML file first.");
        }

        return _root.NewNode(child);
    }

    /// <summary>
    /// 更新或插入一个子节点
    /// </summary>
    /// <param name="child">子节点名称</param>
    /// <param name="value">子节点的文本内容</param>
    public void Upsert<T>(string child, T value) where T : notnull
    {
        if (_root == null)
        {
            throw new NullReferenceException("The root element is empty, please create a new XML file first.");
        }

        _root.Upsert(child, value);
    }
}

/// <summary>
/// 定义一个静态类XmlHelper，提供一些扩展方法
/// </summary>
public static class XmlHelper
{
    /// <summary>
    /// 将XmlDocument对象转换为格式化的XML字符串
    /// </summary>
    /// <param name="doc">XmlDocument对象</param>
    /// <returns>格式化的XML字符串</returns>
    public static string XmlString(this XmlDocument doc)
    {
        MemoryStream memoryStream = new();
        XmlTextWriter writer = new(memoryStream, Encoding.UTF8) { Formatting = Formatting.Indented };
        doc.WriteTo(writer);
        writer.Flush();

        StreamReader streamReader = new(memoryStream);
        memoryStream.Position = 0;
        string xmlString = streamReader.ReadToEnd();
        streamReader.Close();
        memoryStream.Close();
        writer.Dispose();
        return xmlString;
    }

    /// <summary>
    /// 获取指定子节点的文本内容（XmlElement版本）
    /// </summary>
    /// <param name="node">XmlElement对象</param>
    /// <param name="child">子节点名称</param>
    /// <returns>子节点的文本内容</returns>
    public static string Get(this XmlElement node, string child)
    {
        ArgumentException.ThrowIfNullOrEmpty(child, nameof(child));
        XmlNode root = node.SelectSingleNode(child);
        return root?.InnerText;
    }

    /// <summary>
    /// 获取指定子节点的文本内容（XmlNode版本）
    /// </summary>
    /// <param name="node">XmlNode对象</param>
    /// <param name="child">子节点名称</param>
    /// <returns>子节点的文本内容</returns>
    public static string Get(this XmlNode node, string child)
    {
        ArgumentException.ThrowIfNullOrEmpty(child, nameof(child));
        XmlNode root = node.SelectSingleNode(child);
        return root?.InnerText;
    }

    /// <summary>
    /// 获取指定子节点的所有子节点列表
    /// </summary>
    /// <param name="node">XmlElement对象</param>
    /// <param name="child">子节点名称</param>
    /// <returns>子节点列表</returns>
    public static XmlNodeList GetNodes(this XmlElement node, string child)
    {
        ArgumentException.ThrowIfNullOrEmpty(child, nameof(child));
        return node.SelectNodes(child);
    }

    /// <summary>
    /// 设置指定子节点的文本内容
    /// </summary>
    /// <param name="node">XmlElement对象</param>
    /// <param name="child">子节点名称</param>
    /// <param name="value">子节点的文本内容</param>
    /// <returns>设置后的子节点</returns>
    public static XmlElement Set<T>(this XmlElement node, string child, T value) where T : notnull
    {
        ArgumentException.ThrowIfNullOrEmpty(child, nameof(child));
        XmlElement element = node.OwnerDocument.CreateElement(child);
        element.InnerText = value?.ToString() ?? string.Empty;
        node.AppendChild(element);
        return element;
    }

    /// <summary>
    /// 添加一个新的子节点
    /// </summary>
    /// <param name="node">XmlElement对象</param>
    /// <param name="child">子节点名称</param>
    /// <returns>添加后的子节点</returns>
    public static XmlElement NewNode(this XmlElement node, string child)
    {
        ArgumentException.ThrowIfNullOrEmpty(child, nameof(child));
        XmlElement element = node.OwnerDocument.CreateElement(child);
        node.AppendChild(element);
        return element;
    }

    /// <summary>
    /// 更新或插入一个子节点
    /// </summary>
    /// <param name="node">XmlElement对象</param>
    /// <param name="child">子节点名称</param>
    /// <param name="value">子节点的文本内容</param>
    public static void Upsert<T>(this XmlElement node, string child, T value) where T : notnull
    {
        ArgumentException.ThrowIfNullOrEmpty(child, nameof(child));
        XmlNode root = node.SelectSingleNode(child);
        if (root != null)
            root.InnerText = value?.ToString() ?? string.Empty;
        else
            node.Set(child, value);
    }
}